package main

import (
	"fmt"
	"github.com/hkail/lolie/data"
	"math/rand"
	"sort"
	"time"
)

const (
	defaultMaxGameCount   = 5
	defaultMaxPlayerCount = 5
)

var (
	lanes = []string{data.LaneTop, data.LaneJungle, data.LaneMid, data.LaneAdc, data.LaneSup}
)

type Player struct {
	ID   int
	Name string
	Lane string
}

type PlayerLayout struct {
	Players []Player
}

func main() {
	layouts := generateAllLayout([]Player{
		{
			ID:   1,
			Name: "A",
		},
		{
			ID:   2,
			Name: "B",
		},
		{
			ID:   3,
			Name: "C",
		},
		{
			ID:   4,
			Name: "D",
		},
		{
			ID:   5,
			Name: "E",
		},
	}, lanes)

	for _, layout := range layouts {
		sortPlayers(layout.Players)
		fmt.Println(layout.Players)
	}
}

func generateAllLayout(players []Player, lanes []string) []PlayerLayout {
	rand.Seed(time.Now().UnixNano())
	playerLayouts := getDefaultPlayerLayouts()
	rand.Shuffle(len(players), func(i, j int) {
		players[i], players[j] = players[j], players[i]
	})
	rand.Shuffle(len(lanes), func(i, j int) {
		lanes[i], lanes[j] = lanes[j], lanes[i]
	})

	doublePlayerMask := make(map[int]bool)
	playersLaneSet := getDefaultPlayersLaneSet(players)
	gamesPlayerSet := getDefaultGamesPlayerSet()
	gamesLaneSet := getDefaultGamesLaneSet()
	var generateFunc func(curGameCount, curPlayerIndex int) bool
	generateFunc = func(curGameCount, curPlayerIndex int) bool {
		if curGameCount == defaultMaxGameCount {
			return true
		}

		if curPlayerIndex == defaultMaxPlayerCount {
			mask := 0
			for _, player := range playerLayouts[curGameCount].Players {
				if player.ID == 0 {
					return false
				}

				if player.Lane == data.LaneAdc || player.Lane == data.LaneSup {
					mask |= 1 << player.ID
				}
			}

			if doublePlayerMask[mask] {
				return false
			}
			doublePlayerMask[mask] = true

			return generateFunc(curGameCount+1, 0)
		}

		for _, player := range players {
			if gamesPlayerSet[curGameCount][player.ID] {
				continue
			}

			gamesPlayerSet[curGameCount][player.ID] = true
			for _, lane := range lanes {
				if gamesLaneSet[curGameCount][lane] {
					continue
				}

				if playersLaneSet[player.ID][lane] {
					continue
				}

				gamesLaneSet[curGameCount][lane] = true
				playersLaneSet[player.ID][lane] = true
				playerLayouts[curGameCount].Players[curPlayerIndex] = Player{
					ID:   player.ID,
					Name: player.Name,
					Lane: lane,
				}

				if generateFunc(curGameCount, curPlayerIndex+1) {
					return true
				}
				playersLaneSet[player.ID][lane] = false
				gamesLaneSet[curGameCount][lane] = false
			}
			gamesPlayerSet[curGameCount][player.ID] = false
		}

		return false
	}
	generateFunc(0, 0)

	fmt.Println(gamesPlayerSet)
	fmt.Println(gamesLaneSet)

	return playerLayouts
}

func getDefaultPlayerLayouts() []PlayerLayout {
	playerLayouts := make([]PlayerLayout, defaultMaxGameCount)
	for i := 0; i < defaultMaxGameCount; i++ {
		playerLayouts[i].Players = make([]Player, defaultMaxPlayerCount)
	}

	return playerLayouts
}

func getDefaultPlayersLaneSet(players []Player) map[int]map[string]bool {
	playersLaneSet := make(map[int]map[string]bool, len(players))
	for _, player := range players {
		playersLaneSet[player.ID] = make(map[string]bool, len(lanes))
	}

	return playersLaneSet
}

func getDefaultGamesPlayerSet() map[int]map[int]bool {
	gamesPlayerSet := make(map[int]map[int]bool, defaultMaxGameCount)
	for i := 0; i < defaultMaxGameCount; i++ {
		gamesPlayerSet[i] = make(map[int]bool, defaultMaxPlayerCount)
	}

	return gamesPlayerSet
}

func getDefaultGamesLaneSet() map[int]map[string]bool {
	gamesLaneSet := make(map[int]map[string]bool, defaultMaxGameCount)
	for i := 0; i < defaultMaxGameCount; i++ {
		gamesLaneSet[i] = make(map[string]bool, len(lanes))
	}

	return gamesLaneSet
}

func sortPlayers(players []Player) {
	laneWeights := map[string]int{
		data.LaneTop:    0,
		data.LaneJungle: 1,
		data.LaneMid:    2,
		data.LaneAdc:    3,
		data.LaneSup:    4,
	}

	sort.Slice(players, func(i, j int) bool {
		return laneWeights[players[i].Lane] < laneWeights[players[j].Lane]
	})
}

func getAllHeroes() ([]string, error) {
	heroSet := map[string]struct{}{}
	for _, lane := range lanes {
		heroes, err := data.GetHeroes(lane)
		if err != nil {
			return nil, err
		}

		for _, hero := range heroes {
			heroSet[hero] = struct{}{}
		}
	}

	heroes := make([]string, 0, len(heroSet))
	for hero := range heroSet {
		heroes = append(heroes, hero)
	}

	sort.Strings(heroes)

	return heroes, nil
}
